%-*-Prolog-*-

:-dynamic(errorLexico/0).
/* Lista de palabras reservadas */
reservada('IF',iniIf).
reservada('FI',finIf).
reservada('ELSE',relse).
reservada('ELSIF',relsif).
reservada('THEN',then).
reservada('END',fin).
reservada('NULL',nulo).
reservada('WHILE',iniWhile).
reservada('ENDWHILE',finWhile).
reservada('POINTER',puntero).
reservada('FUN',iniFun).
reservada('TIPO',tipo).
reservada('ARRAY',array).
reservada('REC',iniRec).
reservada('ENDREC',finRec).
reservada('OF',de).
reservada('RETURN',retorno).
reservada('RETURNS',retornoFun).
reservada('ALLOC',reservaMem).
reservada('FREE',liberaMem).
reservada('IN',entrada).
reservada('OUT',salida).
reservada('INT',tInt).
reservada('REAL',tReal).
reservada('DO',hacer).

/* Minusculas : ASCII --> a=97, z=122 */
minus(X):-name(X,[Y]),between(97,122,Y).

/* Mayuscula : ASCII --> A=64, Z=90 */
mayus(X):-name(X,[Y]),between(65,90,Y).


/* Letra */
letra(X):-minus(X).
letra(X):-mayus(X).

/* Numero positivo : ASCII --> 1=49, 9=56*/
numPos(X):-name(X,[Y]),between(49,56,Y).

/* Numero */
exponente('e').
exponente('E').
cero('0').
num(X):-numPos(X).
num(X):- cero(X).

/* Pasa el string a lista de caracteres. Para luego devolver a string usar name (ver help(name)). */
aLista([],[]).
aLista([X|Xs],Ys):-name(X,Y),aLista(Xs,Zs),append(Y,Zs,Ys).

/* Funcion de igualdad */
equal(Char,Char).

/* -------------------------------------------------------------------------------------------------------
        Procesamiento texto
   ------------------------------------------------------------------------------------------------------- */
procesa(File, Xs) :-
    open(File, read, In),
    get_char(In, Char),
    listaCaract(Char, In, Xs),
    close(In).

listaCaract(end_of_file, _, []) :- !.
listaCaract(Char,In,[Char|Xs]):-
        get_char(In, Char1),
    listaCaract(Char1,In, Xs).

/* -------------------------------------------------------------------------------------------------------
        Procesamiento lexico. 
   ------------------------------------------------------------------------------------------------------- */

tokenizer(ListaSimbolos,ListaTokens,_,_):-
        init(ListaTokens,_,_,ListaSimbolos,[]).


init(Tokens,Le,Pe) --> {L is 1, P is 1},inicio(Tokens,Le,Pe,L,P).

/* Blancos */
inicio(Tokens,Le,Pe,L,P) -->    [' '],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).
inicio(Tokens,Le,Pe,L,P) -->    ['\t'],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).                                                                       
inicio(Tokens,Le,Pe,L,_) -->    ['\n'],!,{L1 is L + 1, P1 is  1},inicio(Tokens,Le,Pe,L1,P1).                                                                    

/* Identificador */
inicio(Tokens,Le,Pe,L,P,[Car|RCs],Rs):- letra(Car),!,name(Car,Str),append([],Str,Str1),P1 is P + 1,ident(Tokens,Le,Pe,L,P1,Str1,RCs,Rs).

/* Numero entero */
inicio(Tokens,Le,Pe,L,P,[Car|RCs],Rs):- numPos(Car),!,name(Car,Str),append([],Str,Str1),P1 is P + 1,numeroPos(Tokens,Le,Pe,L,P1,Str1,RCs,Rs).
inicio(Tokens,Le,Pe,L,P,[Car|RCs],Rs):- cero(Car),!,name(Car,Str),append([],Str,Str1),P1 is P + 1,numeroCero(Tokens,Le,Pe,L,P1,Str1,RCs,Rs).

/* Punto y coma */
inicio([(pyc,L,P)|Tokens],Le,Pe,L,P) --> [';'],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).

/* Punto y coma */
inicio([(coma,L,P)|Tokens],Le,Pe,L,P) --> [','],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).

/* Parentesis y castings */
inicio(Tokens,Le,Pe,L,P) --> ['('],!,{P1 is P + 1},casting(Tokens,Le,Pe,L,P1).
inicio([(pcierre,L,P)|Tokens],Le,Pe,L,P) --> [')'],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).

/* Corchetes */
inicio([(cap,L,P)|Tokens],Le,Pe,L,P) --> ['['],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).
inicio([(ccierre,L,P)|Tokens],Le,Pe,L,P) --> [']'],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).

/* Sombrero de puntero ( ^ ) */
inicio([(sombrero,L,P)|Tokens],Le,Pe,L,P) --> ['^'],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).

/* Punto (\.) */
inicio([(punto,L,P)|Tokens],Le,Pe,L,P) --> ['.'],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).

/* Operacion || */
inicio(Tokens,Le,Pe,L,P) --> ['|'],!,{P1 is P + 1},o(Tokens,Le,Pe,L,P1).

/* Comentario */
inicio(Tokens,Le,Pe,L,P) --> ['@'],!,{P1 is P + 1},comentario(Tokens,Le,Pe,L,P1).                                                                       

/* Operacion % */
inicio([(mod,L,P)|Tokens],Le,Pe,L,P) --> ['%'],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).                                                                     

% Operacion /
inicio([(div,L,P)|Tokens],Le,Pe,L,P) --> ['/'],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).

/* Operacion * */
inicio([(por,L,P)|Tokens],Le,Pe,L,P) --> ['*'],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).

/* Operacion - */
inicio([(menos,L,P)|Tokens],Le,Pe,L,P) --> ['-'],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).                                                                   

/* Operacion + */
inicio([(mas,L,P)|Tokens],Le,Pe,L,P) --> ['+'],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).

/* Operacion && */
inicio(Tokens,Le,Pe,L,P) --> ['&'],!,{P1 is P + 1},amp(Tokens,Le,Pe,L,P1).                                                                                                                      

/* Operacion >, >= */
inicio(Tokens,Le,Pe,L,P) --> ['>'],!,{P1 is P + 1},mayor(Tokens,Le,Pe,L,P1).

/* Operacion <, <= */
inicio(Tokens,Le,Pe,L,P) --> ['<'],!,{P1 is P + 1},menor(Tokens,Le,Pe,L,P1).

/* Operacion =, == */
inicio(Tokens,Le,Pe,L,P) --> ['='],!,{P1 is P + 1},igual(Tokens,Le,Pe,L,P1).

/* Operacion !, != */
inicio(Tokens,Le,Pe,L,P) --> ['!'],!,{P1 is P + 1},distinto(Tokens,Le,Pe,L,P1).

/* En caso de error */
inicio([],L,P,L,P) --> []. 

/* Operacion =, == */
igual([(igual,L,P)|Tokens],Le,Pe,L,P) --> ['='],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).                                                                                                                                                                                                                    
igual([(asig,L,P)|Tokens],Le,Pe,L,P) --> [],!,inicio(Tokens,Le,Pe,L,P).

/* Operacion !, != */
distinto([(distinto,L,P)|Tokens],Le,Pe,L,P) --> ['='],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).
distinto([(neg,L,P)|Tokens],Le,Pe,L,P) --> [],!,inicio(Tokens,Le,Pe,L,P).

/* Operacion && */
amp([(opy,L,P)|Tokens],Le,Pe,L,P) --> ['&'],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).                                                                                                                                                                                                                        
amp([(amp,L,P)|Tokens],Le,Pe,L,P) --> [],!,inicio(Tokens,Le,Pe,L,P).


/* Operacion || */
o([(opo,L,P)|Tokens],Le,Pe,L,P) --> ['|'],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).                                                                                                                                          
%o([_],Le,Pe,Le,Pe) --> [].

/* Comentario */
comentario(Tokens,Le,Pe,L,_) -->        ['\n'],!,{L1 is L + 1, P1 is 1},inicio(Tokens,Le,Pe,L1,P1).
comentario(Tokens,Le,Pe,L,P) -->        [_],!,{P1 is P+1},comentario(Tokens,Le,Pe,L,P1).
comentario([],L,P,L,P) -->      [],!.

/* Operacion >, >= */
mayor([(mayIg,L,P)|Tokens],Le,Pe,L,P) --> ['='],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).                                                                                                                                                                                                                    
mayor([(mayor,L,P)|Tokens],Le,Pe,L,P) --> [],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).

/* Operacion <, <= */
menor([(menIg,L,P)|Tokens],Le,Pe,L,P) --> ['='],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).                                                                                                                                                                                                                    
menor([(menor,L,P)|Tokens],Le,Pe,L,P) --> [],!,{P1 is P + 1},inicio(Tokens,Le,Pe,L,P1).

/* Casting */
casting([(castint,L,P)|Tokens],Le,Pe,L,P) --> ['i','n','t',')'],!,{P1 is P + 4},inicio(Tokens,Le,Pe,L,P1).                                                                                                                                                                                                                    
casting([(castreal,L,P)|Tokens],Le,Pe,L,P) --> ['r','e','a','l',')'],!,{P1 is P + 5},inicio(Tokens,Le,Pe,L,P1).
casting([(pap,L,P)|Tokens],Le,Pe,L,P) --> [],!,inicio(Tokens,Le,Pe,L,P).

/* Identificador */
ident(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- letra(Car),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,ident(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
ident(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- num(Car),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,ident(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
ident([(Tok,L,P)|Tokens],Le,Pe,L,P,Str,RCs,Rs):- name(Cad,Str),upcase_atom(Cad,Cad1),reservada(Cad1,Tok),!,inicio(Tokens,Le,Pe,L,P,RCs,Rs).
ident([(id(Cad1),L,P)|Tokens],Le,Pe,L,P,Str,RCs,Rs):- name(Cad,Str),upcase_atom(Cad,Cad1),inicio(Tokens,Le,Pe,L,P,RCs,Rs).

/* Numero positivo */
numeroPos(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- num(Car),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,numeroPos(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
numeroPos(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- exponente(Car),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,numeroReal2(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
numeroPos(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- equal(Car,'.'),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,numeroReal1(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
numeroPos(Tokens,Le,Pe,L,P,_,[Car|RCs],_):- letra(Car),!,write('Numero no valido\n'), write(linea-L),nl,write(posicion-P),nl,asserta(errorLexico),inicio(Tokens,Le,Pe,L,P,RCs,Rs).
numeroPos([(int(Cad),L,P)|Tokens],Le,Pe,L,P,Str,RCs,Rs):- name(Cad,Str),inicio(Tokens,Le,Pe,L,P,RCs,Rs).

/* Numero cero. Puede desembocar en numero real */
numeroCero(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- equal(Car,'.'),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,numeroReal1(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
numeroCero([],L,P,L,P,_,[Car|RCs],_):- letra(Car),!,write('Numero no valido\n'), write(linea-L),nl,write(posicion-P),nl,assert(errorLexico),inicio(Tokens,Le,Pe,L,P,RCs,Rs).
numeroCero([],L,P,L,P,_,[Car|RCs],_):- numPos(Car),!,write('Numero no valido\n'), write(linea-L),nl,write(posicion-P),nl,assert(errorLexico),inicio(Tokens,Le,Pe,L,P,RCs,Rs).
numeroCero([(ent(Cad),L,P)|Tokens],Le,Pe,L,P,Str,RCs,Rs):- name(Cad,Str),inicio(Tokens,Le,Pe,L,P,RCs,Rs).

/* Numero real1: Sin exponente, pero el numero aun no es valido (p.e.: 1. 1.000 */
numeroReal1(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- cero(Car),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,numeroReal1(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
numeroReal1(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- numPos(Car),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,numeroReal1a(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
numeroReal1([],L,P,L,P,_,[_|RCs],_):- !,write('Numero no valido\n'), write(linea-L),nl,write(posicion-P),nl,assert(errorLexico),inicio(Tokens,Le,Pe,L,P,RCs,Rs).

/* Numero real1a: Sin exponente, y el numero no finaliza en 0 (es valido)*/
numeroReal1a(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- exponente(Car),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,numeroReal2(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
numeroReal1a(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- cero(Car),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,numeroReal1(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
numeroReal1a(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- numPos(Car),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,numeroReal1a(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
numeroReal1a([],L,P,L,P,_,[Car|RCs],_):- letra(Car),!,write('Numero no valido\n'), write(linea-L),nl,write(posicion-P),nl,assert(errorLexico),inicio(Tokens,Le,Pe,L,P,RCs,Rs).
numeroReal1a([(real(Cad),L,P)|Tokens],Le,Pe,L,P,Str,RCs,Rs):- name(Cad,Str),inicio(Tokens,Le,Pe,L,P,RCs,Rs).

/* Numero real2: Con exponente, pero aun no valido (p.e. 1e 1.5e*/
numeroReal2(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- numPos(Car),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,numeroReal2a(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
numeroReal2(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- equal(Car,'-'),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,numeroReal2b(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
numeroReal2([(real(Cad),L,P)|Tokens],Le,Pe,L,P,Str,[Car|RCs],Rs):- cero(Car),!,name(Car,Str1),append(Str,Str1,Str2),name(Cad,Str2),P1 is P + 1,inicio(Tokens,Le,Pe,L,P1,RCs,Rs).
numeroReal2([],L,P,L,P,_,[_|RCs],_):- !,write('Numero no valido\n'), write(linea-L),nl,write(posicion-P),nl,assert(errorLexico),inicio(Tokens,Le,Pe,L,P,RCs,Rs).

/* Numero real2a: Con exponente y valido (p.e. 1e6 1.5e-700*/
numeroReal2a(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- num(Car),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,numeroReal2a(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
numeroReal2a([],L,P,L,P,_,[Car|RCs],_):- letra(Car),!,write('Numero no valido\n'), write(linea-L),nl,write(posicion-P),nl,assert(errorLexico),inicio(Tokens,Le,Pe,L,P,RCs,Rs).
numeroReal2a([(real(Cad),L,P)|Tokens],Le,Pe,L,P,Str,RCs,Rs):- name(Cad,Str),inicio(Tokens,Le,Pe,L,P,RCs,Rs).

/* Numero real2b: Con exponente negativo y no valido (p.e. 1.5e-*/
numeroReal2b(Tokens,Le,Pe,L,P,Str,[Car|RCs],Rs):- numPos(Car),!,name(Car,Str1),append(Str,Str1,Str2),P1 is P + 1,numeroReal2a(Tokens,Le,Pe,L,P1,Str2,RCs,Rs).
numeroReal2b([(real(Cad),L,P)|Tokens],Le,Pe,L,P,Str,[Car|RCs],Rs):- cero(Car),!,name(Car,Str1),append(Str,Str1,Str2),name(Cad,Str2),P1 is P + 1,inicio(Tokens,Le,Pe,L,P1,RCs,Rs). 


/* -------------------------------------------------------------------------------------------------------
        Funciones y herramientas utiles para el analisis sintactico
   -------------------------------------------------------------------------------------------------------      */
%creaTS(+TablaPadre,-TablaHijo)
creaTS(Ts,Ts).

%anadeID(+TablaEntrada,+ID,+Props,-TablaSalida,-Errores) -- Tenemos que chequear duplicados
anadeID(TS,ID,Props,TS1,E):-unicidadID(TS,ID,E),append(TS,[(ID,Props)],TS1).

%muestraErrores(+Listado de errores).
muestraErrores([]):-!.
muestraErrores([L|Ls]):-write(L),nl,muestraErrores(Ls).

%agrega(+Lin1,+Lin2,-Lout),
agrega([],Ys,Ys).
agrega([X|Xs],Ys,[X|Zs]):-agrega(Xs,Ys,Zs).

%buscaID(+Tabla simbolos, +ID,-Tipo,-Dir,-Error)
buscaID(TS,ID,Tipo,Dir,[]):-member((ID,Tipo,Dir),TS).
buscaID(TS,ID,error,_,E):-	\+ member((ID,_,_),TS),
							E=['El siguiente ID no esta declarado:',ID].

%unicidadID(+Tabla Simbolos, + ID, -Error)
unicidadID(TS,ID,[]):- 	\+ member((ID,_,_),TS).
unicidadID(TS,ID,E):-	member((ID,error,_),TS),
						E=[].			
unicidadID(TS,ID,E):-	member((ID,_,_),TS),
						E=['Variable duplicada:',ID].			

/* -------------------------------------------------------------------------------------------------------
        Tipo de las operaciones
   -------------------------------------------------------------------------------------------------------      */
%tipoDe(+tipo,+operacion,-tipoResultado)
%tipoDe(+tipo1,+tipo2,+operacion,-tipoResultado)
tipoDe(error,_,error):-!.                       /* Para poder subir el error hasta la raiz */
tipoDe(error,_,_,error):-!.
tipoDe(_,error,_,error):-!.
tipoDe(int,[entrada],int):-!.
tipoDe(real,[entrada],real):-!.
tipoDe(int,[salida],int):-!.
tipoDe(real,[salida],real):-!.
tipoDe(_,_,[menor],int).
tipoDe(_,_,[menIg],int).
tipoDe(_,_,[mayor],int).
tipoDe(_,_,[mayIg],int).
tipoDe(_,_,[igual],int).
tipoDe(_,_,[distinto],int).
tipoDe(Int,Int,[mas],int):-Int = int.
tipoDe(Int,Int,[menos],int):-Int = int.
tipoDe(Int,Int,[por],int):-Int = int.
tipoDe(Int,Int,[div],int):-Int = int.
tipoDe(Real,Real,[mas],real):-Real = real.
tipoDe(Real,Real,[menos],real):-Real = real.
tipoDe(Real,Real,[por],real):-Real = real.
tipoDe(Real,Real,[div],real):-Real = real.
tipoDe(_,_,[mas],real).
tipoDe(_,_,[menos],real).
tipoDe(_,_,[por],real).
tipoDe(_,_,[div],real).
tipoDe(int,int,[or],int):-!.
tipoDe(int,int,[and],int):-!.
tipoDe(int,int,[mod],int):-!.
tipoDe(int,[no],int):-!.
tipoDe(Tipo,[menos],Tipo).
tipoDe(_,[castInt],int):-!.
tipoDe(_,[castReal],int):-!.
tipoDe(int,int,[asig],int):-!.
tipoDe(real,_,[asig],real).

%chequeaTipo(+Tipo1,+Tipo2,+Linea Token 2,+Pos Token 2,+operacion,-TipoResultado,-Error)
%chequeaTipo(+Tipo,+Linea Token,+Pos Token,+operacion,-TipoResultado,-Error)
chequeaTipo(Tipo1,Tipo2,Linea,Pos,Operacion,error,Error):-
        \+ tipoDe(Tipo1,Tipo2,Operacion,_),
        Error = ['Error de tipos en coordenadas:',linea:Linea,posicion:Pos].
chequeaTipo(Tipo1,Tipo2,_,_,Operacion,TipoResultado,[]):-   
        tipoDe(Tipo1,Tipo2,Operacion,TipoResultado).
        
chequeaTipo(Tipo,Linea,Pos,Operacion,error,Error):-
        \+ tipoDe(Tipo,Operacion,_),
        Error = ['Error de tipo en coordenadas:\n',linea:Linea,'\n',posicion:Pos].
chequeaTipo(Tipo,_,_,Operacion,TipoResultado,[]):-	tipoDe(Tipo,Operacion,TipoResultado).   
        
%alineamientoTipos(+Tipo1,+Cod1,+Tipo2,+Cod2,-Cod resultado,-Tipo resultado)
alineamientoTipos(error,_,_,_,[],error):-!.
alineamientoTipos(_,_,error,_,[],error):-!.
alineamientoTipos(int,Cod1,real,Cod2,Cod,real):-!,agrega(Cod1,[castReal],Cod11),agrega(Cod11,Cod2,Cod).
alineamientoTipos(real,Cod1,int,Cod2,Cod,real):-!,agrega(Cod2,[castReal],Cod22),agrega(Cod1,Cod22,Cod).
alineamientoTipos(Tipo,Cod1,Tipo,Cod2,Cod,Tipo):-!,agrega(Cod1,Cod2,Cod).
        
   
   /* -------------------------------------------------------------------------------------------------------
        Procesamiento sintactico y semantico
   -------------------------------------------------------------------------------------------------------      */
%sintactico(+ListaTokens)
sintactico(Lt):-prog([],C,Lt,[]),escribe(C).
sintactico(Lt):-prog(Le,_,Lt,_),muestraErrores(Le).

%prog(-Lista errores, -Lista codigo).
prog(E,C) -->   decs(EDecs,TS),
				{Etqh is 1},
                acts(EActs,C,TS,Etqh,_),
                {agrega(EDecs,EActs,E)}.

%decs(-Lista errores, -Tabla de simbolos)                               
decs(E,TS) -->  [],     
                                {creaTS(TSh,[]),
                                 Dirh is 0},
                                rdecs(E,TSh,Dirh,TS).
                                
%rdecs(-Lista errores,+Tabla simbolos heredada,+Direccion heredada,-Tabla de simbolos final)
rdecs(E,TSh,Dirh,TS) -->        dec(EDec,ID,Tipo),              
								[(pyc,_,_)],!,
                                {unicidadID(TSh,ID,Eid),
								 agrega(Eid,EDec,E1),
								 agrega(TSh,[(ID,Tipo,Dirh)],TShrdecs),                                                  
                                 Dirhrdecs is Dirh + 1},
                                rdecs(ERDecs,TShrdecs,Dirhrdecs,TS),
                                {agrega(E1,ERDecs,E)}.
rdecs(E,TSh,Dirh,TS) -->        dec(EDec,ID,Tipo),              
								[(_,L,P)],!,{write("Falta punto y coma"),write(linea(L)),write(posicion(P)),flush,abort}.
rdecs([],TS,_,TS) --> [].

%dec(-Lista errores,-Identificador o lexema,-Tipo del id)
dec(E,ID,Tipo) -->     decvar(E,ID,Tipo),!.                                       
/*                                      
dec(_,_,_) -->  dectipo,!, 
                                [(pyc,_,_)].
dec(_,_,_) -->  decfun,!,
                                [(pyc,_,_)].
*/

%decvar(-Identificador o lexema,-Tipo del id)
decvar([],ID, Tipo) -->	tipo(Tipo,_,_),
						id(ID,_,_),!.
decvar(E,error, error) -->	tipo(Tipo,L,P),
							[(_,L1,P1)],!,{write('Falta el ID'),flush,abort}.
% Posible alternativa al flush current_output(CO), close(CO), 							
/*              
dectipo -->     [(tipo,_,_)],
                        tipo,
                        id(ID,L,P).

decfun -->      [(inifun,_,_)],
                        id(ID,L,P),
                        fparams,
                        bloque,
                        [(fin,_,_)],
                        id(ID,_,_).
                        
bloque -->      decs,
                        acts,

campos -->      campo,
                        [(pyc,_,_)],
                        rcampos.
                        
rcampos --> campo,
                        [(pyc,_,_)],
                        rcampos.
rcampos --> [].

campo --> decvar.

fparams --> [(pap,_,_)],
                        lfparams,
                        [(pcierre,_,_)].
                        
lfparams -->fparam,
                        rlfparams.
lfparams -->[].

rlfparams -->   [(coma,_,_)],
                                fparam,
                                rlfparams.
rlfparams -->   [].

fparam -->      tipo, 
                [(amp,_,_)], 
                id(ID,L,P).
fparam -->      tipo, 
                id(ID,L,P).
*/
%tipo(-Tipo de la declaracion,-Linea de token, - Posicion de token)
tipo(int,L,P) --> [(tInt,L,P)].
tipo(real,L,P) --> [(tReal,L,P)].
/*
tipo --> id(ID,L,P).
tipo -->[(array,_,_)],
                [(cap,_,_)],entpos,[(ccierre,_,_)],
                [(de,_,_)],
                tipo.
tipo -->        [(puntero,_,_)], 
                        tipo().
tipo -->[iniRec], 
                        campos, 
                        [(finRec,_,_)].
*/
%acts(-Lista de errores,-Codigo generado,+Tabla de simbolos,+Etiqueta heredada,+Etiqueta saliente)
acts(E,C,TS,Etqh,Etq) -->act(EAct,CAct,TS,Etqh,Etq1),
                                racts(ERActs,CRActs,TS,Etq1,Etq),
                                {agrega(EAct,ERActs,E),
                                 agrega(CAct,CRActs,C)}.

%racts(-Lista de errores,-Codigo generado,+Tabla de simbolos,+Etiqueta heredada,-Etiqueta saliente)
racts(E,C,TS,Etqh,Etq) -->       act(EAct,CAct,TS,Etqh,Etq1),
                                        racts(ERActs,CRActs,TS,Etq1,Etq),
                                        {agrega(EAct,ERActs,E),
                                         agrega(CAct,CRActs,C)}.
racts([],[],_,Etqh,Etqh) --> [].

%act(-Lista de errores,-Codigo generado,+Tabla de simbolos)
/* act(E,C,TS,Etqh,Etq) --> [(reservaMem,_,_)],
                                entpos,
                                [(pyc,_,_)].
act(E,C,TS,Etqh,Etq) --> [(liberaMem,_,_)], 
                entpos,
                [(pyc,_,_)].
act(E,C,TS,Etqh,Etq) --> iif(E,C,TS),
                [(pyc,_,_)].
act(E,C,TS,Etqh,Etq) --> iif(E,C,TS),
                [(_,L,P)],
                {agrega(E1,['Falta punto y coma:',linea:L,posicion:P],E)}.								
act(E,C,TS,Etqh,Etq) --> iwhile,
                [(pyc,_,_)].
act(E,C,TS,Etqh,Etq) --> [(retorno,_,_)],
                e0,
                [(pyc,_,_)].
*/
act(E,C,TS,Etqh,Etq) --> e0(E,C0,_,TS,_,_),
                                [(pyc,_,_)],
                                {agrega(C0,[borra_cima],C)}.
act(E,[],TS,Etq,Etq) --> e0(E1,_,_,TS,_,_),
				[(_,L,P)],
				{write('Falta punto y coma'),write(linea(L)),write(posicion(P)),!,flush,abort}.				
/*
%iif(-Errores,-Codigo generado,+Tabla de simbolos)
iif(E,C,TS) --> [(iniIf,_,_)],
                e0(E0,C0,T0,TS,L0,P0),
                [(then,_,_)],
                acts(E,C,TS),
                pelse(E,C,TS).

%pelse(-Errores,-Codigo generado,+Tabla de simbolos)                
pelse(E,C,TS) -->       [(relse,_,_)],
                        acts,
                        [(finIf,_,_)].
pelse(E,C,TS) -->       [(relsif,_,_)],
                        e0,
                        [(then,_,_)],
                        acts,
                        pelse.
pelse --> [(finIf,_,_)].

iwhile -->      [(iniWhile,_,_)],
                        e0,
                        [(hacer,_,_)],
                        acts,
                        [(finWhile,_,_)].
*/
%e0(-Lista de errores,-Codigo maquina,-Tipo,+Tabla de simbolos,-Linea coord. inicial, -Pos coord. inicial)
e0(E,C,T,TS,L,P) -->        [(salida,_,_)],
                                        e1(E,Ce1,T,TS,L,P),
                                        {agrega(Ce1,[escribir],C)}.
e0(E,C,T,TS,L,P) --> [(entrada,_,_)],
                                id(ID,L,P), 
                                rid,		
                                {buscaID(TS,ID,Tipo,Dir,E1),
								 Tipo = int,
                                 C=[leer_int(Dir)],
                                 chequeaTipo(Tipo,_,_,[entrada],T,E2),
								 agrega(E1,E2,E)}.
e0(E,C,T,TS,L,P) --> [(entrada,_,_)],
                                id(ID,L,P), 
                                rid,
                                {buscaID(TS,ID,Tipo,Dir,E1),
								 Tipo = real,
                                 C=[leer_real(Dir)],
                                 chequeaTipo(Tipo,_,_,[entrada],T,E2),
								 agrega(E1,E2,E)}.
e0(E,C,T,TS,L,P) --> [(entrada,_,_)], /* ERROR CONTEXTUAL */
                                id(ID,L,P), 
                                rid,
                                {buscaID(TS,ID,Tipo,_,E1),
								 Tipo = error,!,
                                 C=[],
                                 chequeaTipo(Tipo,_,_,[entrada],T,E2),
								 agrega(E1,E2,E)}.								 
e0(E,C,T,TS,L,P) -->        e1(E,C,T,TS,L,P).

%e1(-Lista de errores,-Codigo maquina,-Tipo,+Tabla de simbolos,-Linea coord. inicial, -Pos coord. inicial)
e1(E,C,T,TS,L,P) -->        id(ID,L,P), 
                            rid,
							{buscaID(TS,ID,Tipoh,Dir,E),
                             Codh = [apila_dir(Dir)]},
                            re1(E2,Codh,C,T,TS,ID,Tipoh),
							{agrega(E1,E2,E)}.
e1(E,C,T,TS,L,P) -->        e5otro(E5,C5,Te5,TS,L,P), 
                            re4(Ere4,C5,Cre4,Tre4,TS,Te5), 
                            re3(Ere3,Cre4,Cre3,Tre3,TS,Tre4), 
                            re2(Ere2,Cre3,C,T,TS,Tre3),
                            {agrega(E5,Ere4,E1),
                             agrega(E1,Ere3,E2),
                             agrega(E2,Ere2,E)}.

%re1(-Lista de errores,+Codigo maquina heredado,-Cod maquina generado,-Tipo,+Tabla de simbolos,+Lexema heredado,+Tipo heredado)                          
re1(E,Codh,C,T,TS,Lexh,Tipoh) -->       [(asig,_,_)],
                                                                        e1(Ee1,Ce1,Te1,TS,L,P),
																		{buscaID(TS,Lexh,_,Dir,E1),
																		 chequeaTipo(Tipoh,Te1,L,P,[asig],T,Ere1),
																		 agrega(Ee1,E1,E2),
                                                                         agrega(E2,Ere1,E),
                                                                         Ce2 =[copia_cima(Dir)],                                                                         
																		 agrega(Codh,Ce1,C1),
                                                                         agrega(C1,Ce2,C)}.
re1(E,Codh,C,T,TS,_,Tipoh) -->  re4(Ere4,Codh,Cre4,Tre4,TS,Tipoh), 
                                                re3(Ere3,Cre4,Cre3,Tre3,TS,Tre4), 
                                                re2(Ere2,Cre3,C,T,TS,Tre3),
                                                {agrega(Ere4,Ere3,E1),
                                                 agrega(E1,Ere2,E)}.                                                     

%re2(-Lista de errores,+Codigo maquina heredado,-Cod maquina generado,-Tipo,+Tabla de simbolos,+Tipo heredado) 
re2(E,Codh,C,T,TS,Tipoh) -->op2(CodOp2),
                                                        e3(E3,Code3,Te3,TS,L,P),
                                                        {chequeaTipo(Tipoh,Te3,L,P,CodOp2,_,E1),
                                                         alineamientoTipos(Tipoh,Codh,Te3,Code3,C1,T),
                                                         agrega(E1,E3,E),
                                                         agrega(C1,CodOp2,C)}.
re2([],Codh,Codh,T,_,T) --> [].

%e3(-Lista de errores,-Codigo maquina,-Tipo,+Tabla de simbolos,-Linea coord. inicial, -Pos coord. inicial)
e3(E,C,T,TS,L,P) -->        id(ID,L,P), 
                            rid, 
							{buscaID(TS,ID,Tipo,Dir,E1)},
                            re4(Ere4,[apila_dir(Dir)],Codre4,Tre4,TS,Tipo), 
							re3(Ere3,Codre4,C,T,TS,Tre4),
                                {agrega(E1,Ere4,E2),
								 agrega(E2,Ere3,E)}.
e3(E,C,T,TS,L,P) -->        e5otro(E5,C5,Te5,TS,L,P), 
                                        re4(Ere4,C5,Cre4,Tre4,TS,Te5), 
                                        re3(Ere3,Cre4,C,T,TS,Tre4),
                                        {agrega(E5,Ere4,E1),
                                         agrega(E1,Ere3,E)}.

%re3(-Lista de errores,+Codigo maquina heredado,-Cod maquina generado,-Tipo,+Tabla de simbolos,+Tipo heredado)
re3(E,Codh,C,T,TS,Tipoh) -->op3(CodOp3), 
                                                        e4(E4,C4,Te4,TS,L,P), 
                                                        {chequeaTipo(Tipoh,Te4,L,P,CodOp3,_,Eop3),
                                                         alineamientoTipos(Tipoh,Codh,Te4,C4,C1,Top3),
                                                         agrega(Eop3,E4,E1),
                                                         agrega(C1,CodOp3,Codh1)},
                                                        re3(Ere3,Codh1,C,T,TS,Top3),
                                                        {agrega(E1,Ere3,E)}.
re3([],Codh,Codh,T,_,T) --> [].

%e4(-Lista de errores,-Codigo maquina,-Tipo,+Tabla de simbolos,-Linea coord. inicial, -Pos coord. inicial)
e4(E,C,T,TS,L,P) -->        id(ID,L,P), 
                            rid,
							{buscaID(TS,ID,Tipo,Dir,E1),
                            Codh = [apila_dir(Dir)]},
                            re4(Ere4,Codh,C,T,TS,Tipo),
							{agrega(E1,Ere4,E)}.
e4(E,C,T,TS,L,P) -->        e5otro(E5,C5,Te5,TS,L,P), 
							re4(Ere4,C5,C,T,TS,Te5),
                            {agrega(E5,Ere4,E)}.

%re4(-Lista de errores,+Codigo maquina heredado,-Cod maquina generado,-Tipo,+Tabla de simbolos,+Tipo heredado)
re4(E,Codh,C,T,TS,Tipoh) -->op4(CodOp4), 
                                        e5(E5,C5,Te5,TS,_,_), 
                                        {chequeaTipo(Tipoh,Te5,_,_,CodOp4,_,Eop4),
                                         alineamientoTipos(Tipoh,Codh,Te5,C5,C1,Top4),
                                         agrega(C1,CodOp4,Codre4)},
                                        re4(Ere4,Codre4,C,T,TS,Top4),
                                        {agrega(Eop4,E5,E1),
                                         agrega(E1,Ere4,E)}.                                    
re4([],Codh,Codh,T,_,T) --> [].

%e5(-Lista de errores,-Codigo maquina,-Tipo,+Tabla de simbolos,-Linea coord. inicial,-Pos coord. inicial)
e5(E,[apila_id(ID)],T,TS,L,P) -->  id(ID,L,P),  
									rid,
									{buscaID(TS,ID,T,_,E)}.
e5(E,C,T,TS,L,P) -->        e5otro(E,C,T,TS,L,P).

%e5otro(-Lista de errores,-Codigo maquina,-Tipo,+Tabla de simbolos,-Linea coord. inicial, -Pos coord. inicial)
e5otro(E,C,T,TS,L,P) -->    op5cast(CodOp5cast), 
							e6(Ee6,C6,Te6,TS,L,P),
                            {agrega(C6,CodOp5cast,C),
                            chequeaTipo(Te6,L,P,CodOp5cast,T,Eop5),
                            agrega(Eop5,Ee6,E)}.
e5otro(E,C,T,TS,L,P) -->    op5u(CodOp5u), 
                            e5(Ee5,C5,Te5,TS,L,P),
                            {agrega(C5,CodOp5u,C),
                             chequeaTipo(Te5,L,P,CodOp5u,T,Eop5u),
                             agrega(Eop5u,Ee5,E)}.
e5otro(E,C,T,TS,L,P) -->    e6otro(E,C,T,TS,L,P).

%e6(-Lista de errores,-Codigo maquina,-Tipo,+Tabla de simbolos,-Linea coord. inicial, -Pos coord. inicial)
e6(E,C,T,TS,L,P) --> id(ID,L,P), 
                                rid,
								{buscaID(TS,ID,T,Dir,E),
                                C=[apila_dir(Dir)]}.               
e6(E,C,T,TS,L,P) -->        e6otro(E,C,T,TS,L,P).
/*
rid --> [(sombrero,_,_)],
                [(punto,_,_)], 
                id(ID,L,P),
                rid.
rid --> [(punto,_,_)], 
                id(ID,L,P), 
                rid.
*/
rid --> [].

%e0(-Lista de errores,-Codigo maquina,-Tipo,+Tabla de simbolos,-Linea coord. inicial, -Pos coord. inicial)
e6otro([],[apilaR(V)],real,_,L,P) -->       real(V,L,P).
e6otro([],[apilaI(V)],int,_,L,P) -->        entpos(V,L,P).
e6otro(E,C,T,TS,L,P) -->    [(pap,_,_)],e0(E,C,T,TS,L,P),[(pcierre,_,_)].
e6otro(Error,[],_,_,L,P) -->    [(_,L,P)],{write('Comando no valido\n'),write(linea-L),write(posicion-P),!,flush,abort}.
/*
e6(E,C,T,TS) --> 	ifun(E,C,T,TS).
e6([],C,_,_) --> 	[(nulo,_,_)].

ifun(E,C,T,TS) -->id(ID,L,P), 
                [(pap,_,_)],
                aparams,
                [(pcierre,_,_)].

aparams --> laparams.
aparams --> [].
                        
laparams -->e0,
                        rlaparams.
laparams -->[].

rlaparams -->   [(coma,_,_)],
                                e0,
                                rlaparams.
rlaparams -->   [].
*/

op2([menor]) -->        [(menor,_,_)].
op2([mayor]) -->        [(mayor,_,_)].
op2([menIg]) -->        [(menIg,_,_)].
op2([mayIg]) -->        [(mayIg,_,_)].
op2([igual]) -->        [(igual,_,_)].
op2([distinto]) -->     [(distinto,_,_)].

op3([or]) --> [(opo,_,_)].
op3([mas]) -->[(mas,_,_)].
op3([menos]) -->        [(menos,_,_)].

op4([por]) -->[(por,_,_)].
op4([div]) -->[(div,_,_)].
op4([mod]) -->[(mod,_,_)].
op4([and]) -->[(opy,_,_)].

op5u([menos]) -->       [(menos,_,_)].
op5u([no]) -->  [(neg,_,_)].

%op5cast(-Codigo generado)
op5cast(C) -->  castint(C).
op5cast(C) -->  castreal(C).

castint([castInt]) -->[(castint,_,_)].

castreal([castReal]) -->        [(castreal,_,_)].

/* Ojo: No trabajar con variables anonimas. Puesto asi para debuggear */
entpos(Valor,L,P) -->       [(int(Valor),L,P)].
real(Valor,L,P) -->[(real(Valor),L,P)].
%id(-Lexema,-Linea token,-Posicion token)                            
id(Lex,L,P) -->     [(id(Lex),L,P)].

/* Llamada de pruebas procesa('2.cod',X),tokenizer(X,LT,_,_)*/
probando(Fich):-retractall(errorLexico),procesa(Fich,X),tokenizer(X,LT,_,_),!,continua(LT).

continua(_):-errorLexico,abort.
continua(LT):-sintactico(LT).

escribe(Xs):- tell('out.obj'),escribe1(Xs),told.
escribe1([]).
escribe1([C|Cs]):-write(C),nl,escribe(Cs).
